package 泛型;

//Java 泛型中通配符详解
//
//一、基本概念：
//在学习 Java 泛型的过程中, 通配符是较难理解的一部分. 主要有以下三类:
//
//1、无边界的通配符（Unbounded Wildcards），
//
//  采用 <?> 的形式，比如 List<?>，无边界的通配符的主要作用就是让泛型能够接受未知类型的数据。
//
//2、固定上边界的通配符（Upper Bounded Wildcards）
//
//  使用固定上边界的通配符的泛型，就能够接受指定类及其子类类型的数据。要声明使用该类通配符，采用 <? extends E> 的形式，这里
//  的 E 就是该泛型的上边界。
//  注意：这里虽然用的是 extends 关键字，却不仅限于继承了父类 E 的子类，也可以代指实现了接口 E 的类。
//
//3、固定下边界的通配符（Lower Bounded Wildcards）
//
//  使用固定下边界的通配符的泛型，就能够接受指定类及其父类类型的数据。要声明使用该类通配符，采用 <? super E> 的形式，这里
//  的 E 就是该泛型的下边界。
//
//  注意：你可以为一个泛型指定上边界或下边界，但是不能同时指定上下边界。
//
//二、通配符的基本使用
//如果定义一个方法，该方法用于打印出任意参数化类型的集合中的所有数据，如果这样写：

/*publicclass GernericTest {
    publicstaticvoid main(String[] args) throws Exception{
        List<Integer> listInteger =new ArrayList<Integer>();
        List<String> listString =new ArrayList<String>();
        printCollection(listInteger);
        printCollection(listString);    
    } 
    publicstaticvoid printCollection(Collection<Object> collection){
        for(Object obj:collection){
            System.out.println(obj);
        }  
    }
}*/
//语句 printCollection(listInteger); 报错：
//
//The method printCollection(Collection<Object>) in the type GernericTest is not applicable for the arguments (List<Integer>)
//这是因为泛型的参数是不考虑继承关系就直接报错。这就得用？通配符：

/*publicclass GernericTest {
    publicstaticvoid main(String[] args) throws Exception{
        List<Integer> listInteger =new ArrayList<Integer>();
        List<String> listString =new ArrayList<String>();
        printCollection(listInteger);
        printCollection(listString);
    }
    publicstaticvoid printCollection(Collection<?> collection){
        for(Object obj:collection){
            System.out.println(obj);
        }
    }
}*/
//在方法 printCollection(Collection<?> collection) 中不能出现与参数类型有关的方法比如 collection.add(); 。因为程
//序调用这个方法的时候传入的参数不知道是什么类型的，但是可以调用与参数类型无关的方法比如 collection.size(); 。
//
//注意：使用 ? 通配符可以引用其他各种参数化的类型，? 通配符定义的变量的主要用作引用，可以调用与参数化无关的方法，不能调用与参数化有关的方法。
//
//三、泛型中的 ? 通配符的扩展

//1、界定通配符的上边界
//Vector<? extends 类型1> x = new Vector<类型2>();
//类型1指定一个数据类型，那么类型2就只能是类型1或者是类型1的子类：
//Vector<? extends Number> x = new Vector<Integer>();//这是正确的
//Vector<? extends Number> x = new Vector<String>();//这是错误的
//
//2、界定通配符的下边界
//Vector<? super 类型1> x = new Vector<类型2>();
//类型1指定一个数据类型，那么类型2就只能是类型1或者是类型1的父类
//Vector<? super Integer> x = new Vector<Number>();//这是正确的
//Vector<? super Integer> x = new Vector<Byte>();//这是错误的
//注：限定通配符总是包括自身。
//
//四、通配符使用原则
//我们要记住这么几个使用原则，有人将其称为 PECS（Producer Extends, Consumer Super）原则，翻译过来就
//是 "生产者使用 extends，消费者使用 super"，我觉得还是不翻译的好。也有的地方写作 "in out" 原则，总的来说就是：
//
//·in 或者 producer 就是你要读取出数据以供随后使用（想象一下 List 的 get），这时使用 extends 关键字，固定上边界的通配符。你可以将该对象当做一个只读对象;
//·out 或者 consumer 就是你要将已有的数据写入对象（想象一下 List 的 add），这时使用 super 关键字，固定下边界的通配符。你可以将该对象当做一个只能写入的对象;
//·当你希望 in 或 producer 的数据能够使用 Object 类中的方法访问时, 使用无边界通配符;
//·当你需要一个既能读又能写的对象时，就不要使用通配符了。

//《java泛型 通配符详解及实践》
//观察以下有问题的代码 :
/*public class 泛型通配符{
	public static void main(String[] args) {
		// 编译报错
		// required ArrayList<Integer>, found ArrayList<Number>
		ArrayList<Integer> list1 = new ArrayList<>();
		ArrayList<Number> list2 = list1;//泛型之间没有继承关系

	   // 可以正常通过编译,正常使用
		Integer[] arr1 = new Integer[]{1, 2};
		Number[] arr2 = arr1;//数组是协变的,导致数组能够继承子元素的类型关系
	}
}*/

//上述代码中,在调用print函数时,产生了编译错误 required ArrayList<Integer>, found ArrayList<Number>,说需要的
//是ArrayList<Integer>类型,找到的却是ArrayList<Number>类型, 然后我们知道,Number类是Integer的父类,理论上向上转型,是没有问题的!
//
//而使用java数组类型,就可以向上转型.这是为什么呢???? 原因就在于, Java中泛型是不变的,而数组是协变的.
//
//下面我们来看定义 :

//《不变,协变,逆变的定义》
//逆变与协变用来描述类型转换（type transformation）后的继承关系，其定义：如果A、B表示类型，f(⋅)表示类型转换，≤表示继承关系（比如，A≤B表示A是由B派生出来的子类）；
//
//f(⋅)是逆变（contravariant）的，当A≤B时有f(B)≤f(A)成立；**
//f(⋅)是协变（covariant）的，当A≤B时有f(A)≤f(B)成立；**
//f(⋅)是不变（invariant）的，当A≤B时上述两个式子均不成立，即f(A)与f(B)相互之间没有继承关系**
//由此,可以对上述代码进行解释.
//
//数组是协变的,导致数组能够继承子元素的类型关系 : Number[] arr = new Integer[2]; -> OK
//
//泛型是不变的,即使它的类型参数存在继承关系,但是整个泛型之间没有继承关系 : ArrayList<Number> list = new ArrayList<Integer>(); -> Error
//
//《通配符》
//在java泛型中,引入了 ?(通配符)符号来支持协变和逆变.通配符表示一种未知类型,并且对这种未知类型存在约束关系.
//
//? extends T(上边界通配符upper bounded wildcard) 对应协变关系,表示 ? 是继承自T的任意子类型.也表示一种约束关系,只能提供数据,不能接收数据.
//（? 的默认实现是 ? extends Object, 表示 ? 是继承自Object的任意类型.）
//
//? super T(下边界通配符lower bounded wildcard) 对应逆变关系,表示 ? 是T的任意父类型.也表示一种约束关系,只能接收数据,不能提供你数据.

import java.util.ArrayList;
/*public class 泛型通配符{
    public static void main(String[] args) {
        ArrayList<Integer> list1 = new ArrayList<>();
        list1.add(2);

        // 协变, 可以正常转化, 表示list2是继承 Number的类型
        ArrayList<? extends Number> list2 = list1;
        Object list6=list2;
        System.out.println("list2:"+list2);
        System.out.println("list2[0]:"+list2.get(0));// 编译报错
        // 无法正常添加
        // ? extends Number 被限制为是继承自Number的任意类型,
        // 可能是 Integer,也可能是Float,也可能是其他继承自Number的类,
        // 所以无法将一个确定的类型添加进这个列表,除了null之外
//        list2.add(new Integer(1));// 编译报错
        // 可以添加
        list2.add(null);

        // 逆变
        ArrayList<Number> list3 = new ArrayList<>();
        list3.add(4);
        System.out.println("list3:"+list3);
        
        ArrayList<? super Number> list4 = list3;
//        list4.add(new Integer(1));//已弃用并改为：
        list4.add(Integer.valueOf(1));
//        Number list5=list4;//报错：不能从 ArrayList<capture#5-of ? super Number> 转换为 Number
        Object list5=list4;//Object可以，其他皆不可以。
        System.out.println("list4:"+list4);
        System.out.println("list4:"+list4.get(0));
        System.out.println("list5:"+list5);
    }
}*/

//? 与 T 的差别
//? 表示一个未知类型, T 是表示一个确定的类型. 因此,无法使用 ? 像 T 声明变量和使用变量.如
//    // OK
//    static <T> void test1(List<T> list) {
//        T t = list.get(0);
//        t.toString();
//    }
//    // Error
//    static void test2(List<?> list){
//        ? t = list.get(0);
//        t.toString();
//    }```java
//? 主要针对泛型类的限制, 无法像T类型参数一样单独存在.如
//    // OK
//    static <T> void test1(T t) {
//    }
//    // Error
//    static void test2(? t){
//    }
//? 表示 ? extends Object, 因此它是属于in类型(下面会说明),无法接收数据, 而T可以.
//    // OK
//    static <T> void test1(List<T> list, T t) {
//        list.add(t);
//    }
//    // Error
//    static void test2(List<?> list, Object t) {
//        list.add(t);
//    }
//? 主要表示使用泛型,T表示声明泛型
//泛型类无法使用?来声明,泛型表达式无法使用T
//
//// Error
//public class Holder<?> {
//    ...
//// OK
//public class Holder<T> {
//    ...
//public static void main(String[] args) {
//    // OK
//    Holder<?> holder;
//    // Error
//    Holder<T> holder;
//}
//永远不要在方法返回中使用?,在方法中不会报错,但是方法的接收者将无法正常使用返回值.因为它返回了一个不确定的类型.

//《通配符的使用准则》
//学习使用泛型编程时，更令人困惑的一个方面是确定何时使用上限有界通配符以及何时使用下限有界通配符.
//
//官方文档中提供了一些准则：
//
//"in"类型:
//“in”类型变量向代码提供数据。 如copy（src，dest） src参数提供要复制的数据，因此它是“in”类型变量的参数。
//
//"out"类型:
//“out”类型变量保存接收数据以供其他地方使用.如复制示例中，copy（src，dest），dest参数接收数据，因此它是“out”参数。
//
//"in","out" 准则
//"in" 类型使用 上边界通配符? extends.
//"out" 类型使用 下边界通配符? super.
//如果即需要提供数据(in), 又需要接收数据(out), 就不要使用通配符.
//下面看java源码中Collections类中的copy方法来验证该原则.
//
//    public static <T> void copy(List<? super T> dest, List<? extends T> src) {
//        ...
//        if (srcSize < COPY_THRESHOLD ||
//            (src instanceof RandomAccess && dest instanceof RandomAccess)) {
//            for (int i=0; i<srcSize; i++)
//                // dest 接收数据, src 提供数据
//                dest.set(i, src.get(i));
//        }
//        ...
//    }
//PECS(producer-extends,consumer-super)
//这个是 Effective Java中提出的一种概念.
//
//如果类型变量是生产者,则用 extends ,如果类型变量是消费者,则使用 super. 这种方式也成为 Get and Put Principle.
//即get属于生产者,put属于消费者. 这样的概念比较难懂.
//
//继续使用上述 copy方法的例子.
//
//// dest 消费了数据(set),则使用 super
//// src 生产了数据(get), 则使用 extends
//dest.set(i, src.get(i));
//动手编写通配符函数
//接下来我们通过通配符的知识,来模拟几个在Python语言中很常用的函数.
//
//map() 函数
//在python中,map函数会根据提供的函数对指定序列做映射.
//
//strArr = ["1", "2"]
//intArr = map(lambda x: int(x) * 10, strArr)
//print(strArr,list(intArr))
//# ['1', '2'] [10, 20]
//接下来,我们使用java泛型知识来,实现类似的功能, 方法接收一个类型的列表,可以将其转化为另一种类型的列表.
//
public class Main {
    public static void main(String[] args) {
        List<String> strList = new ArrayList<>();
        strList.add("1");
        strList.add("2");
        // jdk8 使用lambda表达式
        List<Integer> intList = map(strList, s -> Integer.parseInt(s) * 10);
        // strList["1","2"]
        // intList[10,20]
    }

    /**
     * 定义一个接口,它接收一个类型,返回另一个类型.
     *
     * @param <T> 一个类型的方法参数
     * @param <R> 一个类型的返回
     */
    interface Func_TR<T, R> {
        // 接收一个类型,返回另一个类型.
        R apply(T t);
    }

    /**
     * 定义mapping函数
     *
     * @param src    提供数据,因此这里使用(get) 上边界通配符
     * @param mapper mapping 函数的具体实现
     * @param <?     extends R> 提供数据,这里是作为apply的返回值, 因此使用 上边界通配符
     * @param <?     super T>接收数据,这里作为 apply的传入参数
     * @return 返回值不要使用 通配符来定义
     */
    public static <R, T> List<R> map(List<? extends T> src, Func_TR<? super T, ? extends R> mapper) {
        if (src == null)
            throw new IllegalArgumentException("List must not be not null");
        if (mapper == null)
            throw new IllegalArgumentException("map func must be not null");
        // coll 既需要接收数据(add),又需要提供数据(return),所以不使用通配符
        List<R> coll = new ArrayList<>();
        for (T t : src) {
            coll.add(mapper.apply(t));
        }
        return coll;
    }
}
//filter() 函数
//Python中,filter() 函数用于过滤序列，过滤掉不符合条件的元素，返回由符合条件元素组成的新列表。
//
//intArr = [1, 2, 3, 4, 5]
//newArr = filter(lambda x: x >= 3, intArr)
//print(list(newArr))
//# [1, 2, 3, 4, 5] [3, 4, 5]
//接下来,我们使用java泛型知识来,实现类似的功能,方法接收一个列表,和过滤方法,返回过滤后的列表.
//
//public class Main {
//    public static void main(String[] args) {
//        List<Integer> intList = new ArrayList<>();
//        intList.add(1);
//        intList.add(2);
//        intList.add(3);
//        intList.add(4);
//        intList.add(5);
//        
//        List<Integer> filterList = filter(intList, i -> i >= 3);
//        // filterList[3,4,5]
//    }
//    /**
//     * 定义一个接口,它接收一个类型,返回布尔值
//     *
//     * @param <T> 一个类型的方法参数
//     */
//    interface Func_Tb<T> {
//        boolean apply(T t);
//    }
//
//    /**
//     * filter 函数的实现
//     *
//     * @param src  传入的列表只提供数据,这里只调用了迭代操作, 因此使用 上边界通配符
//     * @param func func需要接收一个数据,  因此使用 下边界通配符
//     * @return 返回值不要使用 通配符来定义,返回过滤后的列表
//     */
//    public static <T> List<T> filter(List<? extends T> src, Func_Tb<? super T> func) {
//        if (src == null)
//            throw new IllegalArgumentException("List must not be not null");
//        if (func == null)
//            throw new IllegalArgumentException("filter func must be not null");
//
//        // coll 既需要接收数据(add),又需要提供数据(return),所以不使用通配符
//        List<T> coll = new ArrayList<>();
//        for (T t : src) {
//            if (func.apply(t))
//                coll.add(t);
//        }
//        return coll;
//    }
//}
//reduce()函数
//Python中,reduce() 函数会对参数序列中元素进行累积。
//
//函数将一个数据集合（链表，元组等）中的所有数据进行下列操作：用传给 reduce 中的函数 function（有两个参数）先对集合中的第 1、2 个元素进行操作，得到的结果再与第三个数据用 function 函数运算，最后得到一个结果。
//
//from functools import reduce
//result = reduce(lambda x, y: x + y, [1, 2, 3, 4, 5])
//print(result)
//# 15
//同样的, 我们利用java泛型知识,来实现类似的功能
//
//public class Main {
//    public static void main(String[] args) {
//        List<Integer> intList = new ArrayList<>();
//        intList.add(1);
//        intList.add(2);
//        intList.add(3);
//        intList.add(4);
//        intList.add(5);
//
//        int result = reduce(intList, (t1, t2) -> t1 + t2);
//        // result = 15
//    }
//    /**
//     * 定义一个接口,接收两个同一个类型的参数,返回值也属于同一类型
//     *
//     * @param <T> 作为方法参数,和返回值
//     */
//    interface Func_TTT<T> {
//        T apply(T t1, T t2);
//    }
//
//    /**
//     * reduce函数的实现
//     *
//     * @param src  传入的列表只提供数据,这里只调用了迭代操作, 因此使用 上边界通配符
//     * @param func T 作为 apply()函数的参数和返回值,即接收也提供数据, 因此不能使用通配符
//     * @return 返回值不要使用 通配符来定义, 返回参数相互迭代的值
//     */
//    public static <T> T reduce(List<? extends T> src, Func_TTT<T> func) {
//        if (src == null || src.size() == 0)
//            throw new IllegalArgumentException("List must not be not null or empty");
//        if (func == null)
//            throw new IllegalArgumentException("reduce func must be not null");
//
//        int size   = src.size();
//        T   result = src.get(0);
//        if (size == 1) return result;
//        // 将前两项的值做apply操作后的返回值,再与下一个元素进行操作
//        for (int i = 1; i < size; i++) {
//            T ele = src.get(i);
//            result = func.apply(result, ele);
//        }
//        return result;
//    }
//}


